#Project Diva PS3 BIN parser

import os, sys
from Sanae3D.lib.ModelObject import Model3D
from Sanae3D.lib.StructReader import StructReader

class ProjectDiva_BIN(Model3D):
        
    def __init__(self, obj3D=None, outFile=None, inFile=None):
        
        super(ProjectDiva_BIN,self).__init__("BIN")
        self.obj3D = obj3D
        self.inFile = inFile
        self.outFile = outFile
        self.matSects = {}
        
    def read_string(self):
        
        name = []
        char = self.inFile.read_char()
        while char != "\x00":
            name.append(char)
            char = self.inFile.read_char()
        return ''.join(name)
    
    def parse_materials(self, numMat, offset, sectNum):
        
        self.matSects[sectNum] = []
        self.inFile.seek(offset)
        for i in xrange(numMat):
            self.inFile.seek(8, 1)
            matType = self.inFile.read_string(8, "\x00")
            self.inFile.seek(0x420, 1)
            matName = self.inFile.read_string(0x40, "\x00")
            self.inFile.seek(0x40, 1)
            
            self.matSects[sectNum].append([matName, matType])
            
    def parse_vertices(self, numVerts, meshName):
        
        coords = self.inFile.read_float(3*numVerts)
        for i in xrange(0, numVerts*3, 3):
            vx = coords[i]
            vy = coords[i+1]
            vz = coords[i+2]
            self.create_vertex(meshName, i/3)
            self.add_coords(meshName, i/3, [vx, vy, vz])
            
    def parse_normals(self, numVerts, meshName):
        
        norms = self.inFile.read_float(3*numVerts)
        for i in xrange(0, numVerts*3, 3):
            nx = norms[i]
            ny = norms[i+1]
            nz = norms[i+2]
            self.add_vert_normals(meshName, i/3, [nx, ny, nz])
    
    def parse_uv(self, numVerts, meshName):
        
        uvs = self.inFile.read_float(2*numVerts)
        for i in xrange(0, numVerts*2, 2):
            tu = uvs[i]
            tv = uvs[i+1]
            self.add_vert_uv(meshName, i/2, [tu, tv])
            
    def parse_vert_colors(self, numVerts, meshName):
        
        rgba =  self.inFile.read_float(4*numVerts)
        for i in xrange(0, numVerts*4, 4):
            r = rgba[i]
            g = rgba[i+1]
            b = rgba[i+2]
            a = rgba[i+3]
            
    def parse_weights(self, numVerts, meshName):
        
        weights = self.inFile.read_float(4*numVerts)
        for i in xrange(0, numVerts*4, 4):
            w1 = weights[i]
            w2 = weights[i+1]
            w3 = weights[i+2]
            w4 = weights[i+3]
            
    def parse_vert_bones(self, numVerts, meshName):
        
        bones = self.inFile.read_float(4*numVerts)
        for i in xrange(0, numVerts*4, 4):
            b1 = int(bones[i]) / 3
            b2 = int(bones[i+1]) / 3
            b3 = int(bones[i+2]) / 3
            b4 = int(bones[i+3]) / 3
            
    def parse_faces(self, meshName, numSects, info):
        
        faceNum = 0
        for i in xrange(numSects):
            
            matNum = info[i][4]
            fcount2 = info[i][5]
            foffset2 = info[i][6]
            faceType = info[i][3]           
            self.inFile.seek(foffset2)
            if faceType == 5:
                startDir = -1
                v1, v2 = self.inFile.read_ushort(2)
                faceDir = startDir
                while self.inFile.tell() != foffset2 + (fcount2 * 2):
                    v3 = self.inFile.read_ushort()
                    if v3 == 0xFFFF:
                        v1, v2 = self.inFile.read_ushort(2)
                        faceDir = startDir
                    else:
                        faceDir *= -1
                        if ((v1 != v2) and (v2 != v3) and (v3 != v1)):
                            if faceDir > 0:
                                self.create_face(meshName, faceNum)
                                self.add_face_verts(meshName, faceNum, [v1, v2, v3])
                                self.add_face_material(meshName, faceNum, matNum)
                            else:
                                self.create_face(meshName, faceNum)
                                self.add_face_verts(meshName, faceNum, [v1, v3, v2])
                                self.add_face_material(meshName, faceNum, matNum)
                            faceNum += 1
                        v1 = v2
                        v2 = v3
            elif faceType == 4:
                while self.inFile.tell() != foffset2 + (fcount2 * 2):
                    v1, v2, v3 = self.inFile.read_ushort(3)
                    self.create_face(meshName, faceNum)
                    self.add_face_verts(meshName, faceNum, [v1, v2, v3])
                    self.add_face_material(meshName, faceNum, matNum)
                    faceNum += 1   
            
    def parse_meshes(self, numMesh, offsets, baseOff):
        
        for i in xrange(numMesh):
            
            meshName = offsets[i][0]
            self.create_object(meshName)
            
            faceHeaderOff = offsets[i][2]
            self.inFile.seek(faceHeaderOff)
            numFaceSects = offsets[i][1]
            
            faceOffsets = []
            for j in xrange(numFaceSects):
                
                self.inFile.seek(0x14, 1)
                matNum = self.inFile.read_long()
                self.inFile.seek(0x8, 1)
                
                fcount1 = self.inFile.read_long()
                foffset1 = self.inFile.read_long() + baseOff
                num4, ftype,num1 = self.inFile.read_long(3)
                fcount2 = self.inFile.read_long()
                foffset2 = self.inFile.read_long() + baseOff
                self.inFile.seek(0x20, 1)
                faceOffsets.append([fcount1, foffset1, num4, ftype, matNum,
                                    fcount2, foffset2])
                
            vertStart = offsets[i][4]
            self.inFile.seek(vertStart)
            numVerts = offsets[i][3]
            self.parse_vertices(numVerts, meshName)
            normStart = offsets[i][5]
            self.inFile.seek(normStart)
            self.parse_normals(numVerts, meshName)
            
            uvStart = offsets[i][7]
            self.inFile.seek(uvStart)
            self.parse_uv(numVerts, meshName)
            
            weightStart = offsets[i][8]
            self.inFile.seek(weightStart)
            self.parse_weights(numVerts, meshName)
            
            boneIDStart = offsets[i][9]
            self.inFile.seek(boneIDStart)
            self.parse_vert_bones(numVerts, meshName)
            
            faceStart = offsets[i][10]
            self.inFile.seek(faceStart)
            self.parse_faces(meshName, numFaceSects, faceOffsets)
            
    def parse_bones(self, numSects, info):
        
        boneParents = []
        boneMatrices = []
        boneNames = []
        
        for i in xrange(numSects):
            numBones = info[i][0]
            boneOff1 = info[i][1]
            self.inFile.seek(boneOff1)            
            boneParents = list(self.inFile.read_long(numBones))
                
            boneOff2 = info[i][2]
            self.inFile.seek(boneOff2)
            matrices = self.inFile.read_float(16*numBones)
            for j in xrange(0, numBones*16, 16):
                boneMatrices.append(matrices[j:j+16])
                
            boneOff3 = info[i][3]
            self.inFile.seek(boneOff3)
            boneNameOffsets = self.inFile.read_long(numBones)
                
            for j in xrange(numBones):
                self.inFile.seek(boneNameOffsets[j])
                boneName = self.read_string()
                boneNames.append(boneName)
            
        
    def parse_file(self):
        '''Main parser method. Can be replaced'''
        
        idstring, numSects, numBones, sectTableOfs, boneTableOfs, \
            meshNameOfs = self.inFile.read_long(6)
        meshNameIDOfs1, meshNameIDOfs2, \
                      meshnameIDOfsCount = self.inFile.read_long(3)
        self.inFile.read_long(7)
        
        #load bones
        self.inFile.seek(boneTableOfs)
        boneTableOffsets1 = []        
        for i in xrange(numSects):
            offset = self.inFile.read_long()
            boneTableOffsets1.append(offset)
            
        boneOffsets = []
        for i in xrange(numSects):
            self.inFile.seek(boneTableOffsets1[i])
            boneOff1, boneOff2, boneOff3 = self.inFile.read_long(3)
            null, numBones = self.inFile.read_long(2)
            boneOffsets.append([numBones, boneOff1, boneOff2, boneOff3])
        
        self.parse_bones(numSects, boneOffsets)
        
        #--load skin modifier?
        meshNameOffsets = []
        self.inFile.seek(meshNameOfs)
        for i in xrange(numSects):
            offset = self.inFile.read_long()
            meshNameOffsets.append(offset)
        
        #test
        for i in xrange(numSects):
            offset = meshNameOffsets[i]
            self.inFile.seek(offset)
            self.read_string()
        
        #base offsets
        baseOffsets = []
        self.inFile.seek(sectTableOfs)
        
        for i in xrange(numSects):
            offset = self.inFile.read_long()
            baseOffsets.append(offset)
            
        for i in xrange(numSects):
            offset = baseOffsets[i]
            self.inFile.seek(offset)
            baseOff = self.inFile.tell()
            
            self.inFile.read_long(6)
            numMesh = self.inFile.read_long()
            meshTableStart = self.inFile.read_long() + baseOff
            numMat = self.inFile.read_long()
            matOffset = self.inFile.read_long() + baseOff
            
            self.parse_materials(numMat, matOffset, i)
            
            #mesh file table
            self.inFile.seek(meshTableStart)
            offsetInfo = []
            for j in xrange(numMesh):
                self.inFile.seek(0x14, 1)
                faceSectionCount = self.inFile.read_long()
                faceHeaderOfs = self.inFile.read_long() + baseOff
                C13_17, num0x50 = self.inFile.read_long(2)
                numVerts = self.inFile.read_long()
                vertStart = self.inFile.read_long() + baseOff
                normalStart = self.inFile.read_long() + baseOff
                vertColorStart = self.inFile.read_long() + baseOff
                null = self.inFile.read_long()
                uvStart = self.inFile.read_long() + baseOff
                self.inFile.seek(0x14, 1)
                weightStart = self.inFile.read_long() + baseOff
                boneIDStart = self.inFile.read_long() + baseOff
                faceStart = boneIDStart + (0x10 * numVerts)
                
                self.inFile.seek(0x40, 1)
                meshName = self.inFile.read_string(0x40, "\x00")
                offsetInfo.append([meshName, faceSectionCount, faceHeaderOfs,
                                   numVerts, vertStart, normalStart,
                                   vertColorStart, uvStart, weightStart,
                                   boneIDStart, faceStart])
                
            
            self.parse_meshes(numMesh, offsetInfo, baseOff)
        
        
        
def read_file(path):
    '''Read the file'''
    
    openFile = StructReader(open(path, 'rb'))
    obj = ProjectDiva_BIN(inFile=openFile)
    obj.parse_file()
    openFile.close()
    return obj

def write_file(path):
    
    pass

def definitions():
    '''Return the header, extension, and a description of the format'''
    
    return "\x00\x25\x06\x05", "BIN", ""

if __name__ == '__main__':
    
    file1 = "sakitm000_obj.bin"
    obj = read_file(file1)